//
import { distance, Matrix, Position, Point, Circle } from "common/geom";
import * as mvc from "common/mvc";
import * as dec from "common/decorator";
// ==========================================================================================
import * as dbtps from "../types";
//import Library from "./library";
//import { Armature, Bone, Slot, Display, Animation } from "./dragonBones";
/**
 * 触控信息。
 */
export interface Pointer extends Position {
    event: PointerEvent;
}
/**
 * 汇总触控信息处理再分发。
 */
export default class PointerManager extends mvc.Model {
    public static toString(): string {
        return "[class dragonBones.model.PointerManager]";
    }
    /**
     * 
     */
    public enabled: boolean = true;
    /**
     * 移动速度。
     */
    public readonly moveSpeed: Point = new Point();
    /**
     * 最小圆偏移矩阵。
     */
    public readonly offsetMatrix: Matrix = new Matrix();
    /**
     * 触控是否移动过。
     */
    private _pointerMoved: boolean = false;
    /**
     * 此次触控第一个按下的起始坐标。
     */
    private readonly _firstPointerRawPosition: Point = new Point();
    /**
     * 触控起始的最小圆。
     */
    private readonly _circleRaw: Circle = new Circle();
    /**
     * 触控起始的最小圆。
     */
    private readonly _circleCurrent: Circle = new Circle();
    /**
     * 按顺序按下的 pointer。
     */
    private readonly _pointers: Pointer[] = [];

    private _updateCurrentCircle(): void {
        const pointerCount = this._pointers.length;
        switch (pointerCount) {
            case 1:
                {
                    const pA = this._pointers[0];
                    this._circleCurrent.setTo(pA.x, pA.y, 0.0);
                }
                break;

            case 2:
                {
                    const pA = this._pointers[0];
                    const pB = this._pointers[1];
                    this._circleCurrent.setTo(
                        (pA.x + pB.x) * 0.5,
                        (pA.y + pB.y) * 0.5,
                        distance(pA, pB) * 0.5
                    );
                }
                break;

            case 3:
                {
                    const pA = this._pointers[0];
                    const pB = this._pointers[1];
                    const pC = this._pointers[2];
                    const a = pA.x - pB.x;
                    const b = pA.y - pB.y;
                    const c = pA.x - pC.x;
                    const d = pA.y - pC.y;
                    const e = ((pA.x * pA.x - pB.x * pB.x) + (pA.y * pA.y - pB.y * pB.y)) * 0.5;
                    const f = ((pA.x * pA.x - pC.x * pC.x) + (pA.y * pA.y - pC.y * pC.y)) * 0.5;
                    const det = b * c - a * d;
                    this._circleCurrent.x = -(d * e - b * f) / det;
                    this._circleCurrent.y = -(a * f - c * e) / det;
                    this._circleCurrent.radius = distance(this._circleCurrent, pA);
                }
                break;

            case 0:
            default:
                console.assert(false);
                break;
        }
    }
    /**
     * 添加触控。
     */
    private _updateOffsetMatrix(): void {
        if (this._pointers.length === 1) {
            this.offsetMatrix.identity();
        }
        else {
            const pA = this._pointers[0];
            const scale = this._circleCurrent.radius / this._circleRaw.radius;
            const radian = Math.atan2(this._circleCurrent.y - pA.y, this._circleCurrent.x - pA.x) -
                Math.atan2(this._circleRaw.y - this._firstPointerRawPosition.y, this._circleRaw.x - this._firstPointerRawPosition.x);
            this.offsetMatrix.a = Math.cos(radian) * scale;
            this.offsetMatrix.b = Math.sin(radian) * scale;
            this.offsetMatrix.c = -this.offsetMatrix.b;
            this.offsetMatrix.d = this.offsetMatrix.a;
        }

        this.offsetMatrix.tx = this._circleCurrent.x - this._circleRaw.x;
        this.offsetMatrix.ty = this._circleCurrent.y - this._circleRaw.y;
    }
    /**
     * 添加触控。
     */
    public addPointer(x: number, y: number, event: PointerEvent): void {
        if (this._pointers.length < 3) { // 忽略 3 个点以上的触控。（设备预留手势）
            const pointer: Pointer = { x: x, y: y, event: event };
            if (this._pointers.length === 0) { // 触控开始。
                this._pointerMoved = false;
                this.moveSpeed.clear();
                this._pointers.push(pointer);
                this._circleRaw.setTo(
                    pointer.x,
                    pointer.y,
                    0.0
                );
                this._firstPointerRawPosition.copyFrom(this._circleRaw);
                // 通知开始触控。（用 pointerCount 判断是否开始）
                this._sendNotification(dbtps.MNotificationType.PointerDown, pointer);
            }
            else { // 校正初始最小圆。
                this._updateOffsetMatrix();
                this._pointers.push(pointer);
                this._updateCurrentCircle();
                this._circleRaw.setTo(
                    this._circleCurrent.x - this.offsetMatrix.tx,
                    this._circleCurrent.y - this.offsetMatrix.ty,
                    this._circleCurrent.radius
                );
                // 通知触控点变更。
                this._sendNotification(dbtps.MNotificationType.PointerDown, pointer);
            }
        }
    }
    /**
     * 更新触控。
     */
    public updatePointer(x: number, y: number, event: PointerEvent): void {
        for (let i = 0, l = this._pointers.length; i < l; ++i) {
            const pointer = this._pointers[i];
            if (pointer.event.pointerId !== event.pointerId) {
                continue;
            }
            
            pointer.x = x;
            pointer.y = y;
            pointer.event = event;
            const prevX = this._circleCurrent.x;
            const prevY = this._circleCurrent.y;
            this._updateCurrentCircle(); // 更新当前最小圆。
            this._updateOffsetMatrix(); // 更新最小圆变化矩阵。
            // 通知是否移动。（用 pointerMoved 判断是否第一次移动）
            this._sendNotification(dbtps.MNotificationType.PointerMove, pointer);
            this._pointerMoved = true;
            // 移动速度。
            this.moveSpeed.x += (this._circleCurrent.x - prevX - this.moveSpeed.x) * 0.7;
            this.moveSpeed.y += (this._circleCurrent.y - prevY - this.moveSpeed.y) * 0.7;
            // 是否需要记录旋转和缩放速度？
            break;
        }
    }
    /**
     * 移除触控。
     */
    public removePointer(event: PointerEvent): void {
        for (let i = 0, l = this._pointers.length; i < l; ++i) {
            const pointer = this._pointers[i];
            if (pointer.event.pointerId !== event.pointerId) {
                continue;
            }

            if (this._pointers.length === 1) { // 触控结束。
                this._pointers.splice(i, 1);
                // 通知停止触控。（用 pointerCount 判断是否结束）
                this._sendNotification(dbtps.MNotificationType.PointerUp, pointer);
                this._pointerMoved = false;
                this.moveSpeed.clear();
            }
            else { // 校正初始最小圆。
                this._updateOffsetMatrix();
                this._pointers.splice(i, 1);
                this._updateCurrentCircle();
                this._circleRaw.setTo(
                    this._circleCurrent.x - this.offsetMatrix.tx,
                    this._circleCurrent.y - this.offsetMatrix.ty,
                    this._circleCurrent.radius
                );
                // 通知触控点变更。
                this._sendNotification(dbtps.MNotificationType.PointerUp, pointer);
            }
            break;
        }
    }
    /**
     * 移除所有触控。
     */
    public removeAllPointer(): void {
        let i = this._pointers.length;
        while (i--) {
            this.removePointer(this._pointers[i].event);
        }
    }
    /**
     * 触控是否移动过。
     */
    public get pointerMoved(): boolean {
        return this._pointerMoved;
    }
    /**
     * 当前是几点触控。
     */
    public get pointerCount(): number {
        return this._pointers.length;
    }
}